{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import k3d\n",
    "import numpy as np\n",
    "from math import sqrt, sin, cos\n",
    "from skimage.measure import label\n",
    "\n",
    "width = height = length = 100\n",
    "\n",
    "def r(x, y, z):\n",
    "    r = sqrt((x - width / 2) * (x - width / 2) + (y - height / 2) * (y - height / 2) + (z - length / 2) * (z - length / 2))\n",
    "    r += sin(x / 2) * 3\n",
    "    r += cos(y / 10) * 5\n",
    "    \n",
    "    return r\n",
    "\n",
    "def f(x, y, z):\n",
    "    return 0 if r(x, y, z) > width / 2 else (1 if y + sin(x / 20) * 10 > height / 2 else 2)\n",
    "\n",
    "def on_click(x, y, z):\n",
    "    coords = [x, y, z]\n",
    "    missing = None\n",
    "    missing_count = 0\n",
    "    \n",
    "    for idx, v in enumerate([np.array([0,0,1]), np.array([0,1,0]), np.array([1,0,0])]):\n",
    "        axis = coords[idx]        \n",
    "        \n",
    "        if axis == 0 or (obj.voxels[tuple(np.array([z, y, x]) - v)] == 0):\n",
    "            missing = -v\n",
    "            missing_count += 1\n",
    "\n",
    "        if axis == obj.voxels.shape[2 - idx] - 1 or obj.voxels[tuple(np.array([z, y, x]) + v)] == 0:\n",
    "            missing = v\n",
    "            missing_count += 1 \n",
    "    \n",
    "    if missing_count == 1:        \n",
    "        if missing[0] != 0:\n",
    "            s = obj.voxels[z, :, :]\n",
    "            mask = label(s, connectivity = 2)            \n",
    "            s[mask == mask[y, x]] = plot.voxel_paint_color\n",
    "        if missing[1] != 0:\n",
    "            s = obj.voxels[:, y, :]\n",
    "            mask = label(s, connectivity = 2)\n",
    "            s[mask == mask[z, x]] = plot.voxel_paint_color\n",
    "        if missing[2] != 0:\n",
    "            s = obj.voxels[:, :, x]\n",
    "            mask = label(s, connectivity = 2)\n",
    "            s[mask == mask[z, y]] = plot.voxel_paint_color\n",
    "            \n",
    "        obj.push_data('voxels')\n",
    "        \n",
    "color_map = (0xffff00, 0xff0000, 0x00ff00)\n",
    "voxels = np.array([[[f(x, y, z) for x in range(width)] for y in range(height)] for z in range(length)])\n",
    "\n",
    "obj = k3d.voxels(voxels.astype(np.uint8), color_map)\n",
    "plot = k3d.plot()\n",
    "plot += obj\n",
    "\n",
    "obj.click_callback = on_click\n",
    "plot.voxel_paint_color = 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.display()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot.camera_auto_fit = False\n",
    "plot.mode = 'callback'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
